package com.explorx.sns.puma.im.core.chat.impl;

import com.explorx.sns.puma.im.core.chat.UserInfoDomainService;
import com.explorx.sns.puma.im.core.chat.UserRelationService;
import com.explorx.sns.puma.im.core.chat.consts.UserRelationEnum;
import com.explorx.sns.puma.im.core.chat.model.UserInfoModel;
import com.explorx.sns.puma.im.core.chat.model.UserRelationModel;
import com.explorx.sns.puma.im.core.dal.dao.UserRelationEntityMapper;
import com.explorx.sns.puma.im.core.dal.entity.UserRelationEntity;
import com.explorx.sns.puma.im.core.dal.entity.UserRelationEntityExample;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserRelationServiceImpl implements UserRelationService {

    @Resource
    private UserRelationEntityMapper userRelationEntityMapper;

    @Resource
    private UserInfoDomainService userInfoDomainService;

    @Override
    public UserRelationEnum queryUserRelation(Long from, Long to) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(from)
                .andToUidEqualTo(to);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            return UserRelationEnum.Stranger;
        }
        final UserRelationEntity userRelationEntity = userRelationEntities.get(0);
        return UserRelationEnum.findById(userRelationEntity.getRelation());
    }

    @Override
    public List<UserRelationModel> queryUserIdByRelationType(Long from, UserRelationEnum relationEnum) {
        return getRelationByUidAndRelationType(from, relationEnum);
    }

    private List<UserRelationEntity> queryRelation(Long from, UserRelationEnum relationEnum) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(from)
                .andRelationEqualTo(relationEnum.getValue());
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            return Collections.EMPTY_LIST;
        }
        return userRelationEntities;
    }

    @Override
    public List<UserRelationModel> queryFriendList(Long uid) {
        return getRelationByUidAndRelationType(uid, UserRelationEnum.Friend);
    }

    private List<UserRelationModel> getRelationByUidAndRelationType(Long uid, UserRelationEnum relationEnum) {
        final List<UserRelationEntity> userRelationEntities = queryRelation(uid, relationEnum);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            return Collections.emptyList();
        }
        final Map<Long, UserRelationEntity> userRelationEntityMap = userRelationEntities.stream()
                .collect(Collectors.toMap(UserRelationEntity::getToUid, ele -> ele));
        final List<UserInfoModel> usersInfo = userInfoDomainService.getUsersInfo(Lists.newArrayList(userRelationEntityMap.keySet()));
        return CollectionUtils.isEmpty(usersInfo) ? Collections.EMPTY_LIST
                : usersInfo.stream().map(ele -> UserRelationModel.builder()
                        .userId(ele.getUserId())
                        .avatar(ele.getAvatar())
                        .userName(ele.getUserName())
                        .nickName(ele.getNickName())
                        .createTime(userRelationEntityMap.get(ele.getUserId()).getUpdateTime().getTime()).build())
                .collect(Collectors.toList());
    }

    @Override
    public List<UserRelationModel> queryAllContact(Long uid, Integer pageIdx, Integer pageSize) {

        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(uid)
                .andRelationIn(Lists.newArrayList(UserRelationEnum.Friend.getValue()
                        , UserRelationEnum.Follow.getValue()
                        , UserRelationEnum.Followed.getValue()));
        final List<UserRelationEntity> userRelationFromEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationFromEntities)) {
            return Collections.emptyList();
        }

        final Map<Long, UserRelationEntity> userRelationEntityMap = userRelationFromEntities.stream()
                .collect(Collectors.toMap(UserRelationEntity::getToUid, ele -> ele));
        if (CollectionUtils.isEmpty(userRelationEntityMap)) {
            return Collections.emptyList();
        }

        final List<UserInfoModel> usersInfo = userInfoDomainService.getUsersInfo(Lists.newArrayList(userRelationEntityMap.keySet()));

        return CollectionUtils.isEmpty(usersInfo) ? Collections.EMPTY_LIST
                : usersInfo.stream().map(ele -> UserRelationModel.builder()
                        .relation(userRelationEntityMap.get(ele.getUserId()).getRelation())
                        .userId(ele.getUserId())
                        .avatar(ele.getAvatar())
                        .userName(ele.getUserName())
                        .nickName(ele.getNickName())
                        .createTime(userRelationEntityMap.get(ele.getUserId()).getUpdateTime().getTime()).build())
                .collect(Collectors.toList());
    }

    @Override
    public List<UserRelationModel> queryBlockUserList(Long uid) {
        return getRelationByUidAndRelationType(uid, UserRelationEnum.Block);
    }

    @Override
    public UserRelationModel queryUserProfile(Long fromUid, Long toUid) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(fromUid)
                .andToUidEqualTo(toUid);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities) || Objects.equals(fromUid,toUid)) {
            final UserRelationEntity entity = new UserRelationEntity();
            entity.setToUid(toUid);
            entity.setUpdateTime(new Date());
            userRelationEntities.add(entity);
        }

        assert userRelationEntities.size() == 1;
        final UserRelationEntity entity = userRelationEntities.get(0);
        final UserInfoModel userInfo = userInfoDomainService.getUserInfo(entity.getToUid());
        final int fansCnt = queryFanCount(entity.getToUid());
        final int followCnt=queryFollowCount(entity.getToUid());
        return UserRelationModel.builder()
                .relation(Optional.ofNullable(entity.getRelation()).orElse((byte) 0))
                .fansCnt(fansCnt)
                .followCnt(followCnt)
                .userId(userInfo.getUserId())
                .avatar(userInfo.getAvatar())
                .userName(userInfo.getUserName())
                .nickName(userInfo.getNickName())
                .createTime(entity.getUpdateTime().getTime()).build();
    }

    private int queryFanCount(Long userId){
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andToUidEqualTo(userId)
                .andRelationIn(Lists.newArrayList(UserRelationEnum.Friend.getValue(),UserRelationEnum.Followed.getValue()));
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        return CollectionUtils.isEmpty(userRelationEntities)? 0: userRelationEntities.size();

    }

    private int queryFollowCount(Long userId){
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(userId)
                .andRelationIn(Lists.newArrayList(UserRelationEnum.Friend.getValue(),UserRelationEnum.Follow.getValue()));
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        return CollectionUtils.isEmpty(userRelationEntities)? 0: userRelationEntities.size();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean followUser(Long fromUid, Long targetUid) {
        return updateInsertFollowUser(fromUid, targetUid, UserRelationEnum.Follow) &&
                updateInsertFollowUser(targetUid, fromUid, UserRelationEnum.Followed);
    }

    private boolean updateInsertFollowUser(Long fromUid, Long targetUid, UserRelationEnum relationEnum) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(fromUid)
                .andToUidEqualTo(targetUid);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            UserRelationEntity entity = new UserRelationEntity();
            // 1.0 from to insert
            entity.setRelation(relationEnum.getValue());
            entity.setCreateTime(new Date());
            entity.setUpdateTime(new Date());
            entity.setFollowTime(new Date());
            entity.setFollowedTime(new Date());
            entity.setFromUid(fromUid);
            entity.setToUid(targetUid);
            return userRelationEntityMapper.insert(entity) > 0;
        } else {
            assert userRelationEntities.size() == 1;
            final UserRelationEntity entity = userRelationEntities.get(0);
            entity.setFollowTime(new Date());
            final UserRelationEnum userRelationEnum = UserRelationEnum.findById(entity.getRelation());
            switch (userRelationEnum) {
                case Stranger:
                    entity.setRelation(relationEnum.getValue());
                    return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
                case Block:
                case Blocked:
                    throw new IllegalArgumentException("");
                case Followed:
                    if (relationEnum == UserRelationEnum.Follow) {
                        entity.setRelation(UserRelationEnum.Friend.getValue());
                    }
                    return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
                case Follow:
                    if (relationEnum == UserRelationEnum.Followed) {
                        entity.setRelation(UserRelationEnum.Friend.getValue());
                    }
                    return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
                case Friend:
                    break;
            }
        }
        return true;
    }

    @Override
    public boolean unfollowUser(Long fromUid, Long targetUid) {
        return updateUnFollowUser(fromUid, targetUid, true, UserRelationEnum.Stranger) &&
                updateUnFollowUser(targetUid, fromUid, false, UserRelationEnum.Stranger);
    }

    private boolean updateUnFollowUser(Long fromUid, Long targetUid, boolean fromOperator, UserRelationEnum relationEnum) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(fromUid)
                .andToUidEqualTo(targetUid);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            return true;
        }

        final UserRelationEntity entity = userRelationEntities.get(0);
        entity.setUpdateTime(new Date());
        final UserRelationEnum userRelationEnum = UserRelationEnum.findById(entity.getRelation());
        switch (userRelationEnum) {
            case Stranger:
                break;
            case Block:
            case Blocked:
            case Followed:
                if (!fromOperator) {
                    entity.setRelation(UserRelationEnum.Stranger.getValue());
                }
                return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
            case Follow:
            case Friend:
                if (fromOperator) {
                    entity.setRelation(UserRelationEnum.Followed.getValue());
                } else {
                    entity.setRelation(UserRelationEnum.Follow.getValue());
                }
                return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
        }
        return true;
    }

    private boolean updateInsertBlockUser(Long fromUid, Long targetUid, UserRelationEnum relationEnum) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(fromUid)
                .andToUidEqualTo(targetUid);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        if (CollectionUtils.isEmpty(userRelationEntities)) {
            UserRelationEntity entity = new UserRelationEntity();
            // 1.0 from to insert
            entity.setRelation(relationEnum.getValue());
            entity.setCreateTime(new Date());
            entity.setFromUid(fromUid);
            entity.setToUid(targetUid);
            return userRelationEntityMapper.insert(entity) > 0;
        } else {
            assert userRelationEntities.size() == 1;
            final UserRelationEntity entity = userRelationEntities.get(0);
            entity.setFollowTime(new Date());
            final UserRelationEnum userRelationEnum = UserRelationEnum.findById(entity.getRelation());
            switch (userRelationEnum) {
                case Stranger:
                case Blocked:
                case Follow:
                case Friend:
                    entity.setRelation(relationEnum.getValue());
                    return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
                case Block:
                case Followed:
                    break;
            }
        }
        return true;
    }

    @Override
    public boolean blockUser(Long fromUid, Long targetUid) {
        return updateInsertBlockUser(fromUid, targetUid, UserRelationEnum.Block) && updateInsertBlockUser(targetUid, fromUid, UserRelationEnum.Blocked);
    }

    private boolean updateBlockUser(Long fromUid, Long targetUid, UserRelationEnum relationEnum) {
        UserRelationEntityExample example = new UserRelationEntityExample();
        example.createCriteria().andFromUidEqualTo(fromUid)
                .andToUidEqualTo(targetUid);
        final List<UserRelationEntity> userRelationEntities = userRelationEntityMapper.selectByExample(example);
        assert userRelationEntities.size() == 1;
        final UserRelationEntity entity = userRelationEntities.get(0);
        entity.setFollowTime(new Date());
        final UserRelationEnum userRelationEnum = UserRelationEnum.findById(entity.getRelation());
        switch (userRelationEnum) {
            case Block:
                entity.setRelation(relationEnum.getValue());
                return userRelationEntityMapper.updateByPrimaryKeySelective(entity) > 0;
            case Followed:
            case Stranger:
            case Blocked:
            case Follow:
            case Friend:
                break;
        }

        return true;
    }

    @Override
    public boolean unblockUser(Long fromUid, Long targetUid) {
        return updateBlockUser(fromUid, targetUid, UserRelationEnum.Stranger);
    }

    @Override
    public Pair<Integer, Integer> queryFollowAndFansCnt(Long uid) {
        final int fansCnt = queryFanCount(uid);
        final int followCnt = queryFollowCount(uid);
        return Pair.of(followCnt,fansCnt);
    }
}
